
#include "OLMenuSurface.h"
#include "OLButton.h"
#include "OLApplication.h"
#include "OLMenuPane.h"
#include "OLAppTemplateDefs.h"

#include <QHBoxLayout>
#include <QLabel>
#include <QParallelAnimationGroup>
#include <QPropertyAnimation>
#include <QResizeEvent>

OLMenuSurface::OLMenuSurface(QWidget *parent) :
    OLModalBackground(parent), m_state(NoMenu), m_menupane(0), m_animGroup(0)
{
    m_mapper = new QSignalMapper(this);
    connect(m_mapper, SIGNAL(mapped(int)), this, SLOT(menuItemClicked(int)));
    connect(m_mapper, SIGNAL(mapped(int)), OLApp, SIGNAL(menuItemClicked(int)));
}

int OLMenuSurface::addMenuItem(const QString& menuitem, const QObject* receiver, const char* method)
{
    int randId = qrand();

    addMenuItem(randId, menuitem, receiver, method);

    return randId;
}

void OLMenuSurface::addMenuItem(int id, const QString& menuitem, const QObject* receiver, const char* method)
{
    if(!m_menupane) {
        m_menupane = new OLMenuPane(this);
        connect(this, SIGNAL(portraitSwitch()), m_menupane, (SLOT(handlePortrait())));
        connect(this, SIGNAL(landscapeSwitch()), m_menupane, (SLOT(handleLandscape())));
        connect(this, SIGNAL(clicked()), this, SLOT(hideMenu()));

        changeState(MenuAvailable);
    }

    OLButton* itemBtn = new OLButton(m_menupane);
    itemBtn->textButton(menuitem);

    connect(itemBtn, SIGNAL(clicked()), m_mapper, SLOT(map()));
    m_mapper->setMapping(itemBtn, id);

    if(receiver!=0 && method!=0)
        connect(itemBtn, SIGNAL(clicked()), receiver, method);

    m_menupane->addMenuItem(itemBtn);
}

void OLMenuSurface::removeMenuItem(int id)
{
    QObject* item = m_mapper->mapping(id);
    m_mapper->removeMappings(item);
    delete item;

    QList<OLButton*> menuItems = m_menupane->findChildren<OLButton*>();
    if(menuItems.count()==0) {
        // no more menu items are left
        changeState(NoMenu);
        delete m_menupane;
        m_menupane = 0;
    }
}

void OLMenuSurface::hideMenuItem(int id)
{
    OLButton* item = qobject_cast<OLButton*>(m_mapper->mapping(id));
    if(item) {
        item->hide();
        if(m_menupane->height()<FINGER_SIZE) {
            // The layout should resize the menu pane, if it is less than the height of the
            // button then there are no visible items left
            changeState(NoMenu);
        }
    }
}

void OLMenuSurface::showMenuItem(int id)
{
    OLButton* item = qobject_cast<OLButton*>(m_mapper->mapping(id));
    if(item) {
        item->show();
        if(m_state==NoMenu)
            changeState(MenuAvailable);
    }
}

void OLMenuSurface::showMenu()
{
    // do a first handle of the orientation if required
    if(!m_menupane->layout()) {
        if(OLApp->orientation()==OLApplication::portrait)
            m_menupane->handlePortrait();
        else
            m_menupane->handleLandscape();
    }

    changeState(MenuRequested);
    m_menupane->disableMenuItems();

    if(m_animGroup) {
        // already have an animation running, use the same simply switch direction
        QAbstractAnimation::Direction newDirection = m_animGroup->direction()==QAbstractAnimation::Forward ? QAbstractAnimation::Backward : QAbstractAnimation::Forward;
        m_animGroup->setDirection(newDirection);
    } else {
        // create a new animation
        show();

        m_animGroup = new QParallelAnimationGroup(this);
        //int animDuration = m_menupane->height()+100;
        int animDuration = 350;

        QPropertyAnimation* posAnim = new QPropertyAnimation(m_menupane, "pos", this);
        posAnim->setDuration(animDuration);
        posAnim->setEasingCurve(QEasingCurve::OutQuint);
        posAnim->setStartValue(QPoint(MENUPANE_WINDOWPADDING, -m_menupane->height()));
        posAnim->setEndValue(QPoint(MENUPANE_WINDOWPADDING, MENUPANE_OFFSET));

        m_animGroup->addAnimation(posAnim);

        QPropertyAnimation* opacityAnim = new QPropertyAnimation(m_menupane, "opacity", this);
        opacityAnim->setDuration(animDuration);
        opacityAnim->setEasingCurve(QEasingCurve::Linear);
        opacityAnim->setStartValue(0.0);
        opacityAnim->setEndValue(1.0);

        m_animGroup->addAnimation(opacityAnim);

        m_animGroup->start(QAbstractAnimation::DeleteWhenStopped);

        connect(m_animGroup, SIGNAL(finished()), SLOT(handleAnimFinished()));
    }
}

void OLMenuSurface::hideMenu()
{
    changeState(MenuDismissed);
    m_menupane->disableMenuItems();

    if(m_animGroup) {
        // already have an animation running, use the same simply switch direction
        QAbstractAnimation::Direction newDirection = m_animGroup->direction()==QAbstractAnimation::Forward ? QAbstractAnimation::Backward : QAbstractAnimation::Forward;
        m_animGroup->setDirection(newDirection);
    } else {
        m_animGroup = new QParallelAnimationGroup(this);
        //int animDuration = m_menupane->height()+100;
        int animDuration = 350;

        QPropertyAnimation* posAnim = new QPropertyAnimation(m_menupane, "pos", this);
        posAnim->setDuration(animDuration);
        posAnim->setEasingCurve(QEasingCurve::InQuint);
        posAnim->setStartValue(m_menupane->pos());
        posAnim->setEndValue(QPoint(MENUPANE_WINDOWPADDING, -m_menupane->height()));

        m_animGroup->addAnimation(posAnim);

        QPropertyAnimation* opacityAnim = new QPropertyAnimation(m_menupane, "opacity", this);
        opacityAnim->setDuration(animDuration);
        opacityAnim->setEasingCurve(QEasingCurve::Linear);
        opacityAnim->setStartValue(1.0);
        opacityAnim->setEndValue(0.0);

        m_animGroup->addAnimation(opacityAnim);

        m_animGroup->start(QAbstractAnimation::DeleteWhenStopped);

        connect(m_animGroup, SIGNAL(finished()), SLOT(handleAnimFinished()));
    }
}

void OLMenuSurface::resizeEvent(QResizeEvent * event)
{
    if(m_menupane) {
        m_menupane->setFixedWidth(width()-(MENUPANE_WINDOWPADDING*2));
    }
}

void OLMenuSurface::menuItemClicked(int /*id*/)
{
    hideMenu();
}

void OLMenuSurface::handleAnimFinished()
{
    m_animGroup = 0;

    if(m_state==MenuRequested) {
        changeState(MenuDisplayed);
        m_menupane->enableMenuItems();
    } else {
        changeState(MenuAvailable);
        hide();
    }

}

void OLMenuSurface::changeState(OLMenuState newstate)
{
    m_state = newstate;
    emit menuStateChange(newstate);
}

void OLMenuSurface::handleAppSize()
{
    resize(parentWidget()->size());
}
